%{
#include <stdio.h>
#include "tree_dstruct.h"
#include "generate.h"
#include "code_gen.h"
#include "astree.h"
#include "graph.h"
#include "error.h"
#include "list.h"
#include "register.h"

#ifdef BIS_DBG
#define dbg(...)		printf("# %s: ", __func__), printf(__VA_ARGS__)
#else
#define dbg(...)		
#endif

#define YYERROR_VERBOSE
#define AMOUNT_REG		7

extern int yylex(void);
void yyerror(char const *);
extern long line; /* declared in scanner.l by flex */
int status = 0;
tree_node_t *ast;
symboll_t **zzsymlist;

extern reg_t reg_target; /* declared in register.c */
extern int offset_count; /* declared in register.c */

void free_list(symboll_t**);
void free_list(symboll_t**);
symboll_t** check_labels(symboll_t**);
symboll_t** contains(symboll_t**, char*, long, symbolt_t);
symboll_t** add(symboll_t**, char*, long, symbolt_t);
void print_list(symboll_t**);

int burm_label(NODEPTR_TYPE);
void burm_reduce(NODEPTR_TYPE, int);
tree_node_t *aux_arg_nil;
%}

%union {
	id_wrap_t id;
	num_wrap_t num;
	tree_node_t *parent;
}

%token <id> ID
%token <num> NUM
%token END
%token RETURN
%token GOTO
%token IF
%token VAR
%token AND
%token NOT
%token NEQ

%type <parent> program funcdef pars par stats stat labeldef cond andcond cterm lexpr expr pluslist multlist minuslist term args
%type <parent> END RETURN GOTO IF VAR AND NOT NEQ

%start begin

%%

begin:
	  program				{ ast = $1; }
	;

program:
	  /* empty */				{ $$ = NULL; }
	| program funcdef ';'			{ tree_node_t *c[MC] = {$1, $2, c_lex(';')};
						  $$ = c_node("program", c);
						}
	;

funcdef: 
	  ID '(' pars ')' stats END		{ tree_node_t *c[MC] = {c_id(NULL, $1.name, $1.line, FUNCTION), c_lex('('), $3, c_lex(')'), $5, c_tok(END)};
						  $$ = c_node("funcdef", c);
						}
	;

pars:
	  /* empty */				{ tree_node_t *c[MC] = {NULL};
						  $$ = c_node("pars", c);
						}
	| ID					{ tree_node_t *c[MC] = {c_id(&add, $1.name, $1.line, VARIABLE)};
						  $$ = c_node("pars", c);
						}
	| par ID				{ tree_node_t *c[MC] = {$1, c_id(&add, $2.name, $2.line, VARIABLE)};
						  $$ = c_node("pars", c);
						}
	| par					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("pars", c);
						}
	;

par:
	  ID ','				{ tree_node_t *c[MC] = {c_id(&add, $1.name, $1.line, VARIABLE), c_lex(',')};
						  $$ = c_node("par", c);
						}
	| par ID ','				{ tree_node_t *c[MC] = {$1, c_id(&add, $2.name, $2.line, VARIABLE), c_lex(',')};
						  $$ = c_node("par", c);
						}
	;

stats:
	  /* empty */				{ tree_node_t *c[MC] = {NULL};
						  $$ = c_node("stats", c);
						}
	| stats labeldef stat ';'		{ tree_node_t *c[MC] = {$1, $2, $3, c_lex(';')};
						  $$ = c_node("stats", c);
						}
	;

labeldef:
	  /* empty */				{ tree_node_t *c[MC] = {NULL};
						  $$ = c_node("labeldef", c);
						}
	| labeldef ID ':'			{ tree_node_t *c[MC] = {$1, c_id(&add, $2.name, $2.line, LABEL_DEF), c_lex(':')};
						  $$ = c_node("labeldef", c);
						}
	;

stat:
	  RETURN expr				{ tree_node_t *c[MC] = {c_tok(RETURN), $2};
						  $$ = c_node("stat_ret", c);
						}
	| GOTO ID				{ tree_node_t *c[MC] = {c_tok(GOTO), c_id(&add, $2.name, $2.line, LABEL_REF)};
						  $$ = c_node("stat_goto", c);
						}
	| IF cond GOTO ID			{ tree_node_t *c[MC] = {c_tok(IF), $2, c_tok(GOTO), c_id(&add, $4.name, $4.line, LABEL_REF)};
						  $$ = c_node("stat_cgoto", c);
						}
	| VAR ID '=' expr			{ tree_node_t *c[MC] = {c_tok(VAR), c_id(&add, $2.name, $2.line, VARIABLE), c_lex('='), $4};
						  $$ = c_node("stat_decl", c);
						}
	| lexpr '=' expr			{ tree_node_t *c[MC] = {$1, c_lex('='), $3};
						  $$ = c_node("stat_assign", c);
						}
	| term					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("stat_call", c);
						}
	;

cond:
	  NOT cterm				{ tree_node_t *c[MC] = {c_tok(NOT), $2};
						  $$ = c_node("cond", c);
						  if(c[1]->op_type == NONE)
						  	$$->burm_child[0] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[1];
						  $$->op_type = OPL_NOT;
						}
	| cterm					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("cond", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						}
	| andcond cterm				{ tree_node_t *c[MC] = {$1, $2};
						  $$ = c_node("cond", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OPL_AND;
						}
	;

andcond:
	  andcond cterm AND			{ tree_node_t *c[MC] = {$1, $2, c_tok(AND)};
						  $$ = c_node("andcond", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OPL_AND;
						}
	| cterm AND				{ tree_node_t *c[MC] = {$1, c_tok(AND)};
						  $$ = c_node("andcond", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						}
	;

cterm:
	  '(' cond ')'				{ tree_node_t *c[MC] = {c_lex('('), $2, c_lex(')')};
						  $$ = c_node("cterm", c);
						  if(c[1]->op_type == NONE)
						  	$$->burm_child[0] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[1];
						}
	| expr NEQ expr				{ tree_node_t *c[MC] = {$1, c_tok(NEQ), $3};
						  $$ = c_node("cterm", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[2]->op_type == NONE)
						  	$$->burm_child[1] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[2];
						  $$->op_type = OP_NEQ;
						}
	| expr '>' expr				{ tree_node_t *c[MC] = {$1, c_lex('>'), $3};
						  $$ = c_node("cterm", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[2]->op_type == NONE)
						  	$$->burm_child[1] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[2];
						  $$->op_type = OP_LESS;
						}
	;

lexpr:
	  ID					{ tree_node_t *c[MC] = {c_id(&contains, $1.name, $1.line, VARIABLE)};
						  $$ = c_node("lexpr", c);
						}
	| term '[' expr ']'			{ tree_node_t *c[MC] = {$1, c_lex('['), $3, c_lex(']')};
						  $$ = c_node("lexpr", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[2]->op_type == NONE)
						  	$$->burm_child[1] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[2];

						  $$->op_type = OP_ARR_ADDR;

						}
	;

expr:
	  term					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("expr", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						}
	| pluslist term				{ tree_node_t *c[MC] = {$1, $2};
						  $$ = c_node("expr", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OP_ADD;
						}
	| multlist term				{ tree_node_t *c[MC] = {$1, $2};
						  $$ = c_node("expr", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OP_MUL;
						}
	| minuslist term			{ tree_node_t *c[MC] = {$1, $2};
						  $$ = c_node("expr", c);
						  if(c[0]->op_type == NONE) {
						  	if(c[1]->op_type == NONE)
						  		$$->burm_child[0] = c[1]->burm_child[0];
						  	else
						  		$$->burm_child[0] = c[1];
						  	$$->op_type = OP_UN_NEG;
						  } else {
						  	$$->burm_child[0] = c[1];
						  }
						}
	;

pluslist:
	  term '+'				{ tree_node_t *c[MC] = {$1, c_lex('+')};
						  $$ = c_node("pluslist", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						}
	| pluslist term '+'			{ tree_node_t *c[MC] = {$1, $2, c_lex('+')};
						  $$ = c_node("pluslist", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OP_ADD;
						}
	;

multlist:
	  term '*'				{ tree_node_t *c[MC] = {$1, c_lex('*')};
						  $$ = c_node("multlist", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						}
	| multlist term '*'			{ tree_node_t *c[MC] = {$1, $2, c_lex('*')};
						  $$ = c_node("multlist", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];
						  if(c[1]->op_type == NONE)
						  	$$->burm_child[1] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[1];
						  $$->op_type = OP_MUL;
						}
	;

minuslist:
	  '-'					{ tree_node_t *c[MC] = {c_lex('-')};
						  $$ = c_node("minuslist", c);
						}
	| minuslist '-'				{ tree_node_t *c[MC] = {$1, c_lex('-')};
						  $$ = c_node("minuslist", c);
						  if(c[0]->burm_child[0] == NULL) { /* Eliminate double minus */
						  	$$->burm_child[0] = c[0];
						  	$$->op_type = OP_UN_NEG;
						  }
						}
	;

term:
	  '(' expr ')'				{ tree_node_t *c[MC] = {c_lex('('), $2, c_lex(')')};
						  $$ = c_node("term", c);
						  if(c[1]->op_type == NONE)
						  	$$->burm_child[0] = c[1]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[1];
						}
	| NUM					{ tree_node_t *c[MC] = {c_num($1.val, $1.line)};
						  $$ = c_node("term", c);
						  $$->op_type = TERM_CONST;
						}
	| term '[' expr ']'			{ tree_node_t *c[MC] = {$1, c_lex('['), $3, c_lex(']')};
						  $$ = c_node("term", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[2]->op_type == NONE)
						  	$$->burm_child[1] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[2];

						  $$->op_type = OP_ARR;
						}
	| ID					{ tree_node_t *c[MC] = {c_id(&contains, $1.name, $1.line, VARIABLE)};
						  $$ = c_node("term", c);
						  $$->op_type = TERM_ID;
						}
	| ID '(' args ')'			{ tree_node_t *c[MC] = {c_id(NULL, $1.name, $1.line, FUNCTION), c_lex('('), $3, c_lex(')')};
						  $$ = c_node("term", c);
						  if(c[2]->op_type == NONE)
						  	$$->burm_child[0] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[2];

						  $$->op_type = OP_CALL;
						}
	| ID '(' ')'				{ tree_node_t *c[MC] = {c_id(NULL, $1.name, $1.line, FUNCTION), c_lex('('), c_lex(')')};
						  $$ = c_node("term", c);
						  $$->op_type = OP_CALL_VOID;
						}
	;

args:
	  expr ',' args				{ tree_node_t *c[MC] = {$1, c_lex(','), $3};
	   					  $$ = c_node("args", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  if(c[2]->op_type == NONE)
						  	$$->burm_child[1] = c[2]->burm_child[0];
						  else
						  	$$->burm_child[1] = c[2];

						  $$->op_type = OP_ARG;
	   					}
	| expr ','				{ tree_node_t *c[MC] = {$1, c_lex(',')};
	   					  $$ = c_node("args", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  $$->burm_child[1] = aux_arg_nil;
						  $$->op_type = OP_ARG;
	   					}
	| expr					{ tree_node_t *c[MC] = {$1};
	   					  $$ = c_node("args", c);
						  if(c[0]->op_type == NONE)
						  	$$->burm_child[0] = c[0]->burm_child[0];
						  else
						  	$$->burm_child[0] = c[0];

						  $$->burm_child[1] = aux_arg_nil;
						  $$->op_type = OP_ARG;
	   					}
	;

%%

/*
args:
	  * empty *				{ tree_node_t *c[MC] = {NULL};
						  $$ = c_node("args", c);
						}
	| expr					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("args", c);
						}
	| arg expr				{ tree_node_t *c[MC] = {$1, $2};
						  $$ = c_node("args", c);
						}
	| arg					{ tree_node_t *c[MC] = {$1};
						  $$ = c_node("args", c);
						}
	;

arg:
	  expr ','				{ tree_node_t *c[MC] = {$1, c_lex(',')};
						  $$ = c_node("arg", c);
						}
	| arg expr ','				{ tree_node_t *c[MC] = {$1, $2, c_lex(',')};
						  $$ = c_node("arg", c);
						}
	;

*/

int generate_code_draw_burm(tree_node_t *node, int n) {
	if(node->op_type == NONE) {
		n += draw_gen_graph(LEFT_CHILD(node), n);
	} else {
		n += draw_gen_graph(node, n);
	}
	return n;
}

void generate_code_call_burm(tree_node_t *node) {
	if(node->op_type == NONE) {
		if(burm_label(LEFT_CHILD(node)) == 0) {
			ERR("burm_label() returned 0\n");
		} else {
			burm_reduce(LEFT_CHILD(node), 1);
		}
	} else {
		if(burm_label(node) == 0) {
			ERR("burm_label() returned 0\n");
		} else {
			burm_reduce(node, 1);
		}
	}
	//reg_clean_vars();
}

int generate_code_expr(tree_node_t *node, int n) {
	if(strcmp(node->name, "expr") == 0) {
		generate_code_call_burm(node);
	} else if(node->type == NONTERMINAL) {
		for(int i=0; i<MC; i++) {
			if((node->data.child)[i] != NULL && node->data.child[i]->type == NONTERMINAL) {
				n = generate_code_expr(node->data.child[i], n);
			}
		}
	}
	return n;
}

void generate_code_labels(tree_node_t *node, char *func) {
	if(node->data.child[0] != NULL) {
		generate_code_labels(node->data.child[0], func);
		code(".L_%s_%s:\n", func, node->data.child[1]->data.id.name);
	}
}

int generate_code_cond(tree_node_t *node, int sp, int sn, int labelc, int k, char *func) {
	int node_label = labelc;

	switch(node->op_type) {
		case OPL_NOT:
			if(k) {
				labelc = generate_code_cond(LEFT_CHILD(node), sp, labelc+1, labelc+1, 0, func);
				code("\t jmp .L_%s_%d\n", func, sp);
			} else {
				labelc = generate_code_cond(LEFT_CHILD(node), labelc+1, sn, labelc+1, 1, func);
				code("\t jmp .L_%s_%d\n", func, sn);
			}
			code(".L_%s_%d:\n", func, node_label+1);
			return labelc;
		case OPL_AND:
			labelc = generate_code_cond(LEFT_CHILD(node), sp, sn, labelc, k, func);
			return  generate_code_cond(RIGHT_CHILD(node), sp, sn, labelc, k, func);
		case OP_NEQ:
		case OP_LESS:
			reg_target = R10;
			//printf("# +++++++ #\n");
			//reg_print_all();
			generate_code_call_burm(node->data.child[0]);
			int expr_a = reg_tmp_allocate(R10);

			//reg_print_all();
			reg_target = R11;
			generate_code_call_burm(node->data.child[2]);
			int expr_b = reg_tmp_allocate(R11);
			//printf("# -- \n");

			reg_load2(expr_a, R10, expr_b, R11);
			code("\t cmp %s, %s\n", reg_get_string(reg_get_name(expr_b)), reg_get_string(reg_get_name(expr_a)));
			reg_free(expr_a);
			reg_free(expr_b);
			switch(node->op_type) {
				case OP_NEQ:
					if(k) {
						code("\t je .L_%s_%d\n", func, sp);
					} else {
						code("\t je .L_%s_%d\n", func, sn);
					}
					break;
				case OP_LESS:
					if(k) {
						code("\t jle .L_%s_%d\n", func, sp);
					} else {
						code("\t jle .L_%s_%d\n", func, sn);
					}
					break;
				default:
					ERR("THIS CANNOT HAPPEN\n");
			}
			//reg_print_all();
			//printf("# ------- #\n");
			return labelc;
		default:
			ERR("THIS SHOULD NOT HAPPEN\n");
	}
}

int generate_code_stat(tree_node_t *node, int n, symboll_t **symlist, int *l, int *p, char *func) {
	char *ptr = &(node->name[5]);

	if(strcmp(ptr, "ret") == 0) {
		reg_target = RAX;
		n = generate_code_expr(node, n);
		assert(reg_target == RAX);
		int val = reg_tmp_allocate(reg_target);
		code("\t leave\n");
		code("\t ret\n");
		reg_free(val);
	} else if(strcmp(ptr, "goto") == 0) {
		code("\t jmp .L_%s_%s\n", func, node->data.child[1]->data.id.name);
	} else if(strcmp(ptr, "cgoto") == 0) {
		int tmp_p = (*p);
		if(node->data.child[1]->op_type == NONE) {
			(*p) = 1+generate_code_cond(LEFT_CHILD(node->data.child[1]), (*p), (*p)+1, (*p)+2, 0, func);
			//n += draw_gen_graph(LEFT_CHILD(node->data.child[1]), n);
		} else {
			(*p) = 1+generate_code_cond(node->data.child[1], (*p), (*p)+1, (*p)+2, 0, func);
			//n += draw_gen_graph(node->data.child[1], n);
		}
		code(".L_%s_%d:\n", func, tmp_p);
		code("\t jmp .L_%s_%s\n", func, node->data.child[3]->data.id.name);
		code(".L_%s_%d:\n", func, tmp_p+1);
	} else if(strcmp(ptr, "decl") == 0) {
		dbg("start decl\n");
		symboll_t **ptr = contains(symlist, node->data.child[1]->data.id.name, 0, VARIABLE);

		reg_target = ANY;
		generate_code_call_burm(node->data.child[3]);
		dbg("reg_target %d %s\n", reg_target, reg_get_string(reg_target));
		(*l)++;
		(*ptr)->reg_id = reg_var_allocate(reg_target, -(*l));
		dbg("end decl\n");
	} else if(strcmp(ptr, "assign") == 0) {
		/* switch over ID vs TERM[] */
		dbg("start assign\n");
		if(node->data.child[0]->data.child[0]->type == TERMINAL_ID) {
			symboll_t **ptr = contains(symlist, node->data.child[0]->data.child[0]->data.id.name, 0, VARIABLE);
			dbg("Assign to VAR (%d) %s\n", (*ptr)->reg_id, node->data.child[0]->data.child[0]->data.id.name);
			reg_target = ANY;
			generate_code_call_burm(node->data.child[2]);
			int val = reg_tmp_allocate(reg_target);
			dbg("Var new val in (%d) %s\n", val, reg_get_string(reg_target));
			assert(reg_target == reg_get_name(val));
			reg_var_assign(reg_target, (*ptr)->reg_id);
		} else if(node->data.child[0]->op_type == OP_ARR_ADDR) {
			dbg("Assign to array\n");
			reg_target = ANY;
			generate_code_call_burm(node->data.child[0]);
			int dest = reg_tmp_allocate(reg_target);
			dbg("Array dest is in %s\n", reg_get_string(reg_get_name(dest)));

			reg_target = ANY;
			generate_code_call_burm(node->data.child[2]);
			int val = reg_tmp_allocate(reg_target);
			reg_load2(dest, ANY, val, ANY);
			dbg("Arry new val is in %s\n", reg_get_string(reg_get_name(val)));
			code("\t movq %s, (%s)\n", reg_get_string(reg_get_name(val)), reg_get_string(reg_get_name(dest)));
			reg_free(dest);
			reg_free(val);
		} else {
			ERR("DEAD END\n");
		}
		dbg("end assign\n");
	} else if(strcmp(ptr, "call") == 0) {
		dbg("start call\n");
		assert(node->data.child[0]->op_type == OP_CALL || node->data.child[0]->op_type == OP_CALL_VOID);
		dbg("call %s\n", node->data.child[0]->data.child[0]->data.id.name);
		reg_target = ANY;
		n = generate_code_draw_burm(node->data.child[0], n);
		generate_code_call_burm(node->data.child[0]);
		int ret_val = reg_tmp_allocate(reg_target);
		reg_free(ret_val);
		dbg("end call\n");
	}

	return n;
}

int generate_code_stats(tree_node_t *node, int n, symboll_t **symlist, int *l, int *p, char *func) {
	if(strcmp(node->name, "stats") == 0) {
		if(node->data.child[0] != NULL) {
			n = generate_code_stats(node->data.child[0], n, symlist, l, p, func);
			generate_code_labels(node->data.child[1], func);
			n = generate_code_stat(node->data.child[2], n, symlist, l, p, func);
		}
	} else if(node->type == NONTERMINAL) {
		for(int i=0; i<MC; i++) {
			if((node->data.child)[i] != NULL && node->data.child[i]->type == NONTERMINAL) {
				n = generate_code_stats(node->data.child[i], n, symlist, l, p, func);
			}
		}
	}
	return n;
}

int generate_code_pars(tree_node_t *node, symboll_t **symlist) {
	int k = 0;
	for(int i=0; i<MC; i++) {
		if(node->data.child[i] != NULL) {
			if(node->data.child[i]->type == NONTERMINAL) {
				k += generate_code_pars(node->data.child[i], symlist);
			} else if(node->data.child[i]->type == TERMINAL_ID) {
				symboll_t **ptr = contains(symlist, node->data.child[i]->data.id.name, 0, VARIABLE);
				(*ptr)->reg = k;
				if(k < 6) {
					/* Push them all on the stack */
					(*ptr)->reg_id = reg_pre_allocate(-(k+1));
					code("\t movq %s, %d(%%rbp)\n", reg_get_string(k), -8*(k+1));
				} else {
					/* The next arguments are at %ebp+2, +3, .. */
					(*ptr)->reg_id = reg_pre_allocate(k-4);
				}
				dbg("Assigned %d to %s\n", k, node->data.child[i]->data.id.name);
				return k+1;
			}
		}
	}
	return k;
}

int generate_code_count_vars(symboll_t **ptr) {
	int count = 0;
	for(; *ptr != NULL; ptr = &((*ptr)->next)) {
		if((*ptr)->type == VARIABLE) {
			count++;
		}
	}
	return count;
}

int generate_code_func(tree_node_t *node, int n) {
	if(strcmp(node->name, "funcdef") == 0) {
		tree_node_t *func_id = node->data.child[0];
		code("\t.globl %s\n", func_id->data.id.name);
		code("\t.type %s, @function\n", func_id->data.id.name);
		code("%s:\n", func_id->data.id.name);
		code("\t push %%rbp\n");
		code("\t movq %%rsp, %%rbp\n");

		int k = generate_code_count_vars(&(node->symlist));
		/* Allocate enough stack space for every local variable in this function */
		code("\t addq $%d, %%rsp\n", -k*8);
		offset_count = -k;
		dbg("Setting stack offset to %d\n", -k);
		int l = generate_code_pars(node->data.child[2], &(node->symlist));

		zzsymlist = &(node->symlist);
		int p = 0;
		n = generate_code_stats(node, n, &(node->symlist), &l, &p, node->data.child[0]->data.id.name);

		if(strcmp(node->data.child[4]->data.child[2]->name, "stat_ret") != 0) {
			code("\t leave\n");
			code("\t ret\n");
		}

		code("\n\t.size %s, .-%s\n", func_id->data.id.name, func_id->data.id.name);
		reg_destroy();
	} else if(node->type == NONTERMINAL) {
		for(int i=0; i<MC; i++) {
			if((node->data.child)[i] != NULL && node->data.child[i]->type == NONTERMINAL) {
				n = generate_code_func(node->data.child[i], n);
			}
		}
	}
	return n;
}

void generate_code(tree_node_t *node) {
	code(".text\n");
	generate_code_func(node, 0);
	code("\n");
}

void check_syntax(tree_node_t *node, symboll_t **symlist) {
	symboll_t *symlist_root = NULL;

	if(strcmp(node->name, "funcdef") == 0) {
		symlist = &symlist_root;
	}

	if(node->type == NONTERMINAL) {
		for(int i=0; i<MC; i++) {
			if((node->data.child)[i] != NULL && node->data.child[i]->type == NONTERMINAL) {
				check_syntax((node->data.child)[i], symlist);
			}
		}
	}

	for(int i=0; i<MC; i++) {
		if(node->data.child[i] != NULL) {
			if(node->data.child[i]->type == TERMINAL_ID) {
				if(node->data.child[i]->data.id.syntax_cb != NULL) {
					dbg("Node: %s calling: %s\n", node->name, node->data.child[i]->name);
					(*node->data.child[i]->data.id.syntax_cb)(symlist, node->data.child[i]->data.id.name, node->data.child[i]->data.id.line, node->data.child[i]->data.id.type);
				}
			}
		}
	}

	if(strcmp(node->name, "funcdef") == 0) {
		check_labels(symlist);
		node->symlist = *symlist;
	}
}



void yyerror (char const *s) {
	SET_STATUS(2);
	printf(RED "[Syntax Error]" CLR " before line" GREEN ": %ld " CLR ": %s\n", line, s);
}

//extern void stack_test(void);

int main (int argc, char *argv[]) {
	#if YYDEBUG == 1
	yydebug = 1;
	#endif
	//stack_test();
	//return;

	aux_arg_nil = c_lex('0');
	aux_arg_nil->op_type = OP_ARG_NIL;

	yyparse();

	if(status == 0) {
		check_syntax(ast, NULL);
		if(status == 0) {
			draw_graph_out("digraph tree {\n");
			draw_graph(ast, 0);
			draw_graph_out("}\n");

			draw_gen_graph_out("digraph tree {\n");
			generate_code(ast);
			draw_gen_graph_out("}\n");
		}
	}

	dbg(RED "[Exit]" CLR GREEN ": %d\n" CLR, status);
	return status;
}
